package mdt.figure;

import org.tigris.gef.base.*;
import org.tigris.gef.presentation.*;
import mdt.program.MDTGraphModel;
import mdt.model.IConnector;
import mdt.model.IEdgeModel;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Enumeration;

public class SelectionRerouteEdge extends SelectionEdgeClarifiers {

    /**
     * Used to determine if the association is now to self,
     * in which case The association needs automatic layout.
     */
    private FigNode sourceFig;

    /**
     * Used to determine if the association is now to self,
     * in which case The association needs automatic layout.
     */
    private FigNode destFig;

    /**
     * The re-routing capability it armed if the mouse was previously
     * dragged.
     * <p>prevents just selecting the message then clicking somewhere
     * else on the diagram,
     */
    private boolean armed;

    /**
     * The index of the point on the line of the message.
     * <p>0 = sender end
     * <p>1..* = receiver end
     */
    private int pointIndex;

    /**
     * Creates a new instance of SelectionRerouteEdge
     *
     * @param feme the given Fig
     */
    public SelectionRerouteEdge(Fig feme) {
        // TODO: There is a cyclic dependency between SelectionRerouteEdge
        // and FigEdgeModelElement
        super(feme);

        // set it to an invalid number by default
        // to make sure it is set correctly.
        pointIndex = -1;
    }

    /**
     * Set up for re-routing.
     * <p>
     * {@inheritDoc}
     */
    public void mousePressed(MouseEvent me) {

        // calculate the source and dest figs for to self assoc
        sourceFig =
                ((FigEdge) getContent()).getSourceFigNode();
        destFig =
                ((FigEdge) getContent()).getDestFigNode();

        final Rectangle mousePosition =
                new Rectangle(me.getX() - 5, me.getY() - 5, 10, 10);
        //reset the pointIndex
        pointIndex = -1;
        final int npoints = getContent().getNumPoints();
        final int[] xs = getContent().getXs();
        final int[] ys = getContent().getYs();
        if (mousePosition.contains(xs[0], ys[0])) {
            pointIndex = 0;
        } else if (mousePosition.contains(
                xs[npoints - 1], ys[npoints - 1])) {
            pointIndex = npoints - 1;
        }

        super.mousePressed(me);
    }

    /*
     * Need to 'arm' the rerouting capability with mouseDragged().
     * <p>
     * Don't arm if the edtior's current mode is a figedge create mode,
     * because once a new edge has been created it is not deselected,
     * therefore on the next create an unwanted reroute is performed.
     *
     * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
     */
    public void mouseDragged(MouseEvent me) {

        Editor editor = Globals.curEditor();
        ModeManager modeMgr = editor.getModeManager();
        FigModifyingMode fMode = modeMgr.top();

        if (!(fMode instanceof ModeCreatePolyEdge)) {
            armed = true;
        }
        super.mouseDragged(me);
    }

    /*
     * Perform re-routing if src/dest nodes have changed.
     *
     * <p>This method needs to be 'armed' by a previous mouseDragged()
     * to avoid the situation where the user just clicks on the shapeNode
     * then clicks on some unrelated Fig, without moving the edge...
     *
     * <p>TODO: improve the fig finding algorithm to find the top most fig
     * in the layer. will be useful for nested states in a statechart.
     *
     * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
     */
    public void mouseReleased(MouseEvent me) {
        // check pre-conds
        if (me.isConsumed() || !armed || pointIndex == -1) {
            armed = false;
            super.mouseReleased(me);
            return;
        }

        //Set-up:
        int x = me.getX(), y = me.getY();
        // the fig that was under the mouse when it was released
        Fig newFig = null;
        //make a nice little target area:
        Rectangle mousePoint = new Rectangle(x - 5, y - 5, 5, 5);
        // and find the Fig:
        Editor editor = Globals.curEditor();
        LayerManager lm = editor.getLayerManager();
        Layer active = lm.getActiveLayer();
        Enumeration figs = active.elementsIn(mousePoint);
        // last is the top fig.
        while (figs.hasMoreElements()) {
            Fig candidateFig = (Fig) figs.nextElement();

            if (getContent() == candidateFig) continue;
            if (candidateFig instanceof FigNetworkDevice
                    || candidateFig instanceof FigDiamondChoice
                    || candidateFig instanceof FigEdge
                    && candidateFig.isSelectable()) {
                newFig =  candidateFig;
                break;
            }

        }

        MDTGraphModel graphModel = (MDTGraphModel) editor.getGraphModel();

        FigNode oldFig = null;
        boolean isSource = false;
        if (pointIndex == 0) {
            oldFig = sourceFig;
            isSource = true;
        } else {
            oldFig = destFig;
        }

        if (oldFig instanceof FigAnchorNode) { //nothing changed, just remove the anchor node
            if (oldFig.getGroup() instanceof FigEdge) {
                if (newFig == oldFig.getGroup()) { //target on the same edge
                    ((FigEdge)newFig).updateEdgeConnector((FigAnchorNode)oldFig, x, y);
                }
            } else {
                oldFig.setLocation(x, y);
                oldFig.updateEdges();
            }
            armed = false;
            super.mouseReleased(me);
            return;
        }

        IConnector connector = null;
        boolean newFigCreated = false;
        if (newFig instanceof FigNode) {
            FigNode newFigNode = (FigNode)newFig;
            connector = (IConnector)newFigNode.deepHitPort(x, y);
        } else if (newFig instanceof FigEdge) {
            newFig = ((FigEdge)newFig).createEdgeConnector(x, y);
            editor.add(newFig);
            connector = (IConnector)((FigNode)newFig).deepHitPort(x, y);
            newFigCreated = true;
        }

        // check intermediate post-condition.
        if (newFig == null
                || connector == null
                || !newFig.getClass().equals(oldFig.getClass())) {
            if (newFigCreated) {
                editor.remove(newFig);
            }
            armed = false;
            super.mouseReleased(me);
            return;
        }

        // delegate the re-routing to graphmodels.
        IEdgeModel edgeModel = (IEdgeModel)getContent().getOwner();
        IConnector oldConnector = isSource ? edgeModel.getSrcConnector() : edgeModel.getDstConnector();
        if (connector != oldConnector && graphModel.canChangeConnectedPort(connector, oldConnector, edgeModel)) {
            graphModel.changeConnectedPort(connector, oldConnector, edgeModel, isSource);
        }

        editor.getSelectionManager().deselect(getContent());
        armed = false;
        // TODO: There is a cyclic dependency between SelectionRerouteEdge
        // and FigEdgeModelElement
        FigMessage figEdge = (FigMessage) getContent();

        figEdge.determineFigNodes(); //TODO

        figEdge.computeRoute();
        super.mouseReleased(me);
        return;
    }


}

